package srv

import (
	"context"
	"fmt"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/asserts"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/auths"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/beans"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/ferror"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/gorms"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/loggers"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/scalars"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/sqls"
	"gitee.com/lailonghui/vehicle-supervision-framework/pkg/xids"
	"gitee.com/shiqiyue/xd-bi/internal/modules/cr/gqlgen/mgr/gmodel"
	"gitee.com/shiqiyue/xd-bi/internal/modules/cr/model"
	"gitee.com/shiqiyue/xd-bi/pkg/queues"
	jsoniter "github.com/json-iterator/go"
	"github.com/opentracing/opentracing-go"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"sync"
	"time"
)

func ReportExecInit() *ReportExecSrv {
	srv := &ReportExecSrv{}
	srv.insertCache = queues.NewQueue(512)
	srv.lock = &sync.Mutex{}
	err := beans.ProvideBean(srv)
	asserts.Nil(err, err)
	return srv
}

type ReportExecSrv struct {
	Db *gorm.DB `inject:""`

	insertCache *queues.EsQueue

	lock *sync.Mutex
}

func (s *ReportExecSrv) StartScheduleCacheFlush() {
	tick := time.Tick(5 * time.Second)
	go func() {
		for {
			select {
			case <-tick:
				s.refreshInsertCache()
			}
		}
	}()
}

// 开始执行
func (s *ReportExecSrv) Start(ctx context.Context, reportId string, params map[string]interface{}) (*model.ReportExec, error) {
	pstr, err := jsoniter.MarshalToString(params)
	if err != nil {
		return nil, ferror.Wrap("序列化入参异常", err)
	}

	now := time.Now()
	r := &model.ReportExec{
		ExecId:    xids.GetXid(ctx),
		StartTime: now,
		ReportId:  reportId,
		Params:    pstr,
		UserId:    scalars.StringIfNilDefault(auths.GetUserId(ctx), ""),
		ExecDate:  now,
	}
	span := opentracing.SpanFromContext(ctx)
	if span != nil {
		textMap := opentracing.TextMapCarrier{}
		err := opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, textMap)
		if err == nil {
			for k, v := range textMap {
				if k == "uber-trace-id" {
					r.TraceId = v
				}
			}
		}

	}
	return r, nil
}

func (s *ReportExecSrv) ExecSuccess(ctx context.Context, reportExec *model.ReportExec) error {
	now := time.Now()
	reportExec.EndTime = now
	reportExec.UseTime = int64(time.Since(reportExec.StartTime) / time.Millisecond)
	reportExec.Success = true
	return s.save(ctx, reportExec)
}

func (s *ReportExecSrv) save(ctx context.Context, reportExec *model.ReportExec) error {
	putSuccess, _ := s.insertCache.Put(reportExec)
	if putSuccess {
		return nil
	} else {
		s.refreshInsertCache()
	}

	return nil
}

func (s *ReportExecSrv) ExecFail(ctx context.Context, reportExec *model.ReportExec, err error) error {
	errorStr := fmt.Sprintf("%+v\n", err)
	now := time.Now()
	reportExec.EndTime = now
	reportExec.UseTime = int64(time.Since(reportExec.StartTime) / time.Millisecond)
	reportExec.Success = false
	reportExec.ErrorMsg = errorStr
	return s.save(ctx, reportExec)
}

func (s *ReportExecSrv) refreshInsertCache() {
	defer func() {
		if err := recover(); err != nil {
			loggers.Error("刷新执行结果到数据库异常", nil)
		}
	}()
	s.lock.Lock()
	defer s.lock.Unlock()
	ctx := context.Background()
	rs := make([]*model.ReportExec, s.insertCache.Quantity())
	n, _ := s.insertCache.Gets(rs)
	if n == 0 {
		return
	}
	db := gorms.GetDb(ctx, s.Db)
	err := db.Model(&model.ReportExec{}).CreateInBatches(&rs, 50).Error
	if err != nil {
		loggers.Error("刷新执行结果到数据库异常", ctx, zap.Error(err))
	}

}

func (s *ReportExecSrv) Page(ctx context.Context, req gmodel.ReportExecPageQuery) ([]*model.ReportExec, int, error) {
	db := gorms.GetDb(ctx, s.Db)
	queryParam := make(map[string]interface{}, 0)
	if req.ReportID != nil {
		queryParam["report_id"] = req.ReportID
	}
	if req.StartTime != nil {
		queryParam["start_time"] = req.StartTime
	}
	if req.EndTime != nil {
		queryParam["end_time"] = req.EndTime
	}
	if req.Success != nil {
		queryParam["success"] = req.Success
	}
	if req.UseTimeMin != nil {
		queryParam["use_time_min"] = req.UseTimeMin
	}
	if req.UseTimeMax != nil {
		queryParam["use_time_max"] = req.UseTimeMax
	}
	if req.UserID != nil {
		queryParam["user_id"] = req.UserID
	}

	if req.OrderColumn != nil && *req.OrderColumn != "" {
		if *req.OrderBy == "descending" {
			queryParam["OrderByDirection"] = "desc"
		} else {
			queryParam["OrderByDirection"] = "asc"
		}
		switch *req.OrderColumn {
		case "UseTime":
			queryParam["OrderByColumn"] = "use_time"
		case "StartTime":
			queryParam["OrderByColumn"] = "start_time"
		case "EndTime":
			queryParam["OrderByColumn"] = "end_time"
		case "Success":
			queryParam["OrderByColumn"] = "success"
		}
	} else {
		queryParam["OrderByColumn"] = "id"
		queryParam["OrderByDirection"] = "desc"
	}
	var c int
	rs := make([]*model.ReportExec, 0)
	err := sqls.Page(ctx, db, sqls.PageParam{
		ListSql:     &DictCategoryPageSql,
		CountSql:    &DictCategoryCountSql,
		M:           queryParam,
		CurrentPage: req.CurrentPage,
		PageSize:    req.PageSize,
	}, &c, &rs)
	if err != nil {
		return nil, 0, err
	}
	return rs, c, nil
}

func (s *ReportExecSrv) ReportDailySuccessRate(ctx context.Context, req gmodel.ReportStatisticsReq) ([]*gmodel.DailyRate, error) {

	return nil, nil
}
